# 14. TypeScript学习

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统对 ES6 的支持,它由 Microsoft 开发。

# 基础

# 1. 原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。

let isDone: boolean = false;

let anyThing: any = 'hello';//任意值(未指定其类型,那么它会被识别为任意值类型)

function getLength(something: string | number): number {
    return something.length;
}
1
2
3
4
5
6
7

# 2. 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:

function alertName(): void {
  alert('My name is Tom');
}

let unusable: void = undefined;
1
2
3
4
5

# 3. undefined 和 null 是所有类型的子类型:

// 这样不会报错
let num: number = undefined;
1
2

# 4. 类型推论

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

let myFavoriteNumber;//设为any类型
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
1
2
3
4
5
6
7

# 5. 联合类型

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
1
2
3

# 6. 接口(Interfaces)是一个很重要的概念,它是对行为的抽象:

//不能新增也不能缺少属性
interface Person {
    name: string;
    age: number;
}
let tom: Person = {
    name: 'Tom',
    age: 25
};

//可以缺少age
interface Person {
    name: string;
    age?: number;
}
let tom: Person = {
    name: 'Tom'
};

//任意属性
interface Person {
    name: string;
    age?: number;
    run(): void;
    [propName: string]: any;
}
let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

//只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 7. 数组类型

let fibonacci: number[] = [1, 1, 2, 3, 5];

let fibonacci: Array<number> = [1, 1, 2, 3, 5];
1
2
3

# 8. 常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:

function sum() {
    let args: IArguments = arguments;
}
1
2
3

其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:

interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}
1
2
3
4
5

# 9. 函数的类型

//输入多余的(或者少于要求的)参数,是不被允许的:
function sum(x: number, y: number): number {
    return x + y;
}
sum(1, 2, 3);
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.

sum(1);
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.

//可选参数(可选参数必须接在必需参数后面)
function buildName(firstName: string, lastName?: string) {}

function push(array: any[], ...items: any[]) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 10. 函数表达式

let mySum = function (x: number, y: number): number {
    return x + y;
};


let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

//用接口定义函数的形状
interface SearchFunc {
    (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 11. 重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}
1
2
3
4
5
6
7
8
9

TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

# 12. 类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。
值 as 类型
<类型>值

(window as any).foo = 1;


interface ApiError extends Error {
    code: number;
}
interface HttpError extends Error {
    statusCode: number;
}
function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 13. 声明文件

export const name: string;
export function getName(): string;
export class Animal {
    constructor(name: string);
    sayHi(): string;
}
export enum Directions {
    Up,
    Down,
    Left,
    Right
}
export interface Options {
    data: any;
}

// types/foo/index.d.ts
declare const name: string;
declare function getName(): string;
declare class Animal {
    constructor(name: string);
    sayHi(): string;
}
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
interface Options {
    data: any;
}
export { name, getName, Animal, Directions, Options };

// types/foo/index.d.ts
export default function foo(): string;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 14. 内置对象

Boolean、Error、Date、RegExp 等。
Document、HTMLElement、Event、NodeList

# 进阶

# 1. 类型别名

类型别名用来给一个类型起个新名字。

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}
1
2
3
4
5
6
7
8
9
10

# 2. 元组

let tom: [string, number] = ['Tom', 25];
1

# 3. 类

TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。

public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的 private 修饰的属性或方法是私有的,不能在声明它的类的外部访问 protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

class Animal {
  public name;
  // public name: string;
  // readonly name;
  public constructor(name) {
    this.name = name;
  }
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
  private name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';

// index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
// index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
  protected name;
  public constructor(name) {
    this.name = name;
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name);
    console.log(this.name);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 抽象类

abstract 用于定义抽象类和其中的抽象方法。

抽象类是不允许被实例化的,抽象类中的抽象方法必须被子类实现:

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}
class Cat extends Animal {
  public sayHi() {
    console.log(`Meow, My name is ${this.name}`);
  }
}
let cat = new Cat('Tom');
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5. implements

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

//类实现接口
interface Alarm {
    alert(): void;
}

interface Light {
    lightOn(): void;
    lightOff(): void;
}

class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

接口继承接口

interface Alarm {
    alert(): void;
}
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
1
2
3
4
5
6
7

接口继承类

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 6. 泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']
1
2
3
4
5
6
7
8
9

上例中,我们在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]
1
2
3
4
5

# 7. 泛型约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
1
2
3
4
5
6
7
8

上例中,我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。

# 8. 泛型类

与泛型接口类似,泛型也可以用于类的类型定义中:

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
1
2
3
4
5
6
7
8

# 9. 泛型参数的默认类型

在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
1
2
3
4
5
6
7

# 10. 声明合并

如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型:

interface Alarm {
    price: number;
}
interface Alarm {
    weight: number;
}
//相当于:
interface Alarm {
    price: number;
    weight: number;
}


interface Alarm {
    price: number;
}
interface Alarm {
    price: string;  // 类型不一致,会报错
    weight: number;
}
// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type.  Variable 'price' must be of type 'number', but here has type 'string'.


interface Alarm {
    price: number;
    alert(s: string): string;
}
interface Alarm {
    weight: number;
    alert(s: string, n: number): string;
}
//相当于:
interface Alarm {
    price: number;
    weight: number;
    alert(s: string): string;
    alert(s: string, n: number): string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38